home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / awksrc.zip / MAIN.C < prev    next >
C/C++ Source or Header  |  1993-09-28  |  18KB  |  720 lines

  1. /*
  2.  * main.c -- Expression tree constructors and main program for gawk. 
  3.  */
  4.  
  5. /* 
  6.  * Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
  7.  * 
  8.  * This file is part of GAWK, the GNU implementation of the
  9.  * AWK Progamming Language.
  10.  * 
  11.  * GAWK is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  * 
  16.  * GAWK is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  * 
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with GAWK; see the file COPYING.  If not, write to
  23.  * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  */
  25.  
  26. #include "awk.h"
  27. #include "patchlevel.h"
  28.  
  29. extern int getopt( int, char *const    *, const char *); 
  30.  
  31. static void usage P((void));
  32. static void copyleft P((void));
  33. static void cmdline_fs P((char *str));
  34. static void init_args P((int argc0, int argc, char *argv0, char **argv));
  35. static void init_vars P((void));
  36. static void pre_assign P((char *v));
  37. SIGTYPE catchsig P((int sig, int code));
  38. static void gawk_option P((char *optstr));
  39. static void nostalgia P((void));
  40. static char *gawk_name P((char *filespec));
  41.  
  42. #ifdef MSDOS
  43. extern int getopt P((int argc, char **argv, char *optstring));
  44. extern int isatty P((int));
  45. #endif
  46.  
  47. /* These nodes store all the special variables AWK uses */
  48. NODE *FS_node, *NF_node, *RS_node, *NR_node;
  49. NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
  50. NODE *CONVFMT_node;
  51. NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
  52. NODE *ENVIRON_node, *IGNORECASE_node;
  53. NODE *ARGC_node, *ARGV_node;
  54. NODE *FIELDWIDTHS_node;
  55.  
  56. int NF;
  57. int NR;
  58. int FNR;
  59. int IGNORECASE;
  60. char *FS;
  61. char *RS;
  62. char *OFS;
  63. char *ORS;
  64. char *OFMT;
  65. char *CONVFMT;
  66.  
  67. /*
  68.  * The parse tree and field nodes are stored here.  Parse_end is a dummy item
  69.  * used to free up unneeded fields without freeing the program being run 
  70.  */
  71. int errcount = 0;    /* error counter, used by yyerror() */
  72.  
  73. /* The global null string */
  74. NODE *Nnull_string;
  75.  
  76. /* The name the program was invoked under, for error messages */
  77. const char *myname;
  78.  
  79. /* A block of AWK code to be run before running the program */
  80. NODE *begin_block = 0;
  81.  
  82. /* A block of AWK code to be run after the last input file */
  83. NODE *end_block = 0;
  84.  
  85. int exiting = 0;        /* Was an "exit" statement executed? */
  86. int exit_val = 0;        /* optional exit value */
  87.  
  88. #if defined(YYDEBUG) || defined(DEBUG)
  89. extern int yydebug;
  90. #endif
  91.  
  92. char **srcfiles = NULL;        /* source file name(s) */
  93. int numfiles = -1;        /* how many source files */
  94. char *cmdline_src = NULL;    /* if prog is on command line */
  95.  
  96. int strict = 0;            /* turn off gnu extensions */
  97. int do_posix = 0;        /* turn off gnu extensions and \x */
  98. int do_lint = 0;        /* provide warnings about questionable stuff */
  99. int in_begin_rule = 0;        /* we're in a BEGIN rule */
  100. int in_end_rule = 0;        /* we're in a END rule */
  101.  
  102. int output_is_tty = 0;        /* control flushing of output */
  103.  
  104. extern char *version_string;    /* current version, for printing */
  105.  
  106. NODE *expression_value;
  107.  
  108. /*
  109.  * for strict to work, legal options must be first
  110.  *
  111.  * Unfortunately, -a and -e are orthogonal to -c.
  112.  *
  113.  * Note that after 2.13, c,a,e,C,D, and V go away.
  114.  */
  115. /* the + on the front is for GNU getopt */
  116. #ifdef DEBUG
  117. char awk_opts[] = "+F:f:v:W:caeCVD";
  118. #else
  119. char awk_opts[] = "+F:f:v:W:caeCV";
  120. #endif
  121.  
  122. extern void resetup P((void));
  123.  
  124. int
  125. main(argc, argv)
  126. int argc;
  127. char **argv;
  128. {
  129.     int c;
  130.     char *scan;
  131.     extern int optind;
  132.     extern char *optarg;
  133.     int i;
  134.     int do_nostalgia;
  135.  
  136.     (void) signal(SIGFPE, (void *) (SIGTYPE (*) P((int))) catchsig);
  137.     (void) signal(SIGSEGV, (void *) (SIGTYPE (*) P((int))) catchsig);
  138. #ifndef MSDOS
  139. //     (void) signal(SIGBUS,  (SIGTYPE (*) P((int))) catchsig);
  140. #endif
  141.  
  142.     myname = gawk_name(argv[0]);
  143.         argv[0] = (char *)myname;
  144. #ifdef VMS
  145.     vms_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
  146. #endif
  147.     if (argc < 2)
  148.         usage();
  149.  
  150.     /* remove sccs gunk */
  151.     if (strncmp(version_string, "@(#)", 4) == 0)
  152.         version_string += 4;
  153.  
  154.     /* initialize the null string */
  155.     Nnull_string = make_string("", 0);
  156.     Nnull_string->numbr = 0.0;
  157.     Nnull_string->type = Node_val;
  158.     Nnull_string->flags = (PERM|STR|STRING|NUM|NUMBER);
  159.  
  160.     /* Set up the special variables */
  161.  
  162.     /*
  163.      * Note that this must be done BEFORE arg parsing else -F
  164.      * breaks horribly 
  165.      */
  166.     init_vars();
  167.  
  168.     /* worst case */
  169.     emalloc(srcfiles, char **, argc * sizeof(char *), "main");
  170.     srcfiles[0] = NULL;
  171.  
  172.     /* undocumented feature, inspired by nostalgia, and a T-shirt */
  173.     do_nostalgia = 0;
  174.     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  175.         if (argv[i][1] == '-')        /* -- */
  176.             break;
  177.         else if (argv[i][1] == 'c') {    /* compat not in next release */
  178.             do_nostalgia = 0;
  179.             break;
  180.         } else if (STREQ(&argv[i][1], "nostalgia"))
  181.             do_nostalgia = 1;
  182.             /* keep looping, in case -c after -nostalgia */
  183.     }
  184.     if (do_nostalgia) {
  185.         fprintf(stderr, "%s, %s\n",
  186.         "warning: option -nostalgia will go away in a future release",
  187.         "use -W nostalgia");
  188.         nostalgia();
  189.         /* NOTREACHED */
  190.     }
  191.     /* Tell the regex routines how they should work. . . */
  192.     resetup();
  193.  
  194.     while ((c = getopt (argc, argv, awk_opts)) != EOF) {
  195.         switch (c) {
  196. #ifdef DEBUG
  197.         case 'D':
  198.             fprintf(stderr,
  199. "warning: option -D will go away in a future release, use -W parsedebug\n");
  200.             gawk_option("parsedebug");
  201.             break;
  202. #endif
  203.  
  204.         case 'c':
  205.             fprintf(stderr,
  206.     "warning: option -c will go away in a future release, use -W compat\n");
  207.             gawk_option("compat");
  208.             break;
  209.  
  210.         case 'F':
  211.             cmdline_fs(optarg);
  212.             break;
  213.  
  214.         case 'f':
  215.             /*
  216.              * a la MKS awk, allow multiple -f options.
  217.              * this makes function libraries real easy.
  218.              * most of the magic is in the scanner.
  219.              */
  220.             /* The following is to allow for whitespace at the end
  221.              * of a #! /bin/gawk line in an executable file
  222.              */
  223.             scan = optarg;
  224.             while (isspace(*scan))
  225.                 scan++;
  226.             if (*scan == '\0')
  227.                 srcfiles[++numfiles] = argv[optind++];
  228.             else
  229.                 srcfiles[++numfiles] = optarg;
  230.             break;
  231.  
  232.         case 'v':
  233.             pre_assign(optarg);
  234.             break;
  235.  
  236.         case 'V':
  237.             warning(
  238.         "option -V will go away in a future release, use -W version");
  239.             gawk_option("version");
  240.             break;
  241.  
  242.         case 'C':
  243.             warning(
  244.         "option -C will go away in a future release, use -W copyright");
  245.             gawk_option("copyright");
  246.             break;
  247.  
  248.         case 'a':    /* use old fashioned awk regexps */
  249.             warning("option -a will go away in a future release");
  250.             break;
  251.  
  252.         case 'e':    /* use Posix style regexps */
  253.             warning("option -e will go away in a future release");
  254.             break;
  255.  
  256.         case 'W':       /* gawk specific options */
  257.             gawk_option(optarg);
  258.             break;
  259.  
  260.         case '?':
  261.         default:
  262.             /* getopt will print a message for us */
  263.             /* S5R4 awk ignores bad options and keeps going */
  264.             break;
  265.         }
  266.     }
  267.  
  268. #ifdef DEBUG
  269.     setbuf(stdout, (char *) NULL);    /* make debugging easier */
  270. #endif
  271.     if (isatty(fileno(stdout)))
  272.         output_is_tty = 1;
  273.     /* No -f option, use next arg */
  274.     /* write to temp file and save sourcefile name */
  275.     if (numfiles == -1) {
  276.         if (optind > argc - 1)    /* no args left */
  277.             usage();
  278.         cmdline_src = argv[optind];
  279.         optind++;
  280.     }
  281.     srcfiles[++numfiles] = NULL;
  282.     init_args(optind, argc, (char *) myname, argv);
  283.     (void) tokexpand();
  284.  
  285.     /* Read in the program */
  286.     if (yyparse() || errcount)
  287.         exit(1);
  288.  
  289.     /* Set up the field variables */
  290.     init_fields();
  291.  
  292.     if (begin_block) {
  293.         in_begin_rule = 1;
  294.         (void) interpret(begin_block);
  295.     }
  296.     in_begin_rule = 0;
  297.     if (!exiting && (expression_value || end_block))
  298.         do_input();
  299.     if (end_block) {
  300.         in_end_rule = 1;
  301.         (void) interpret(end_block);
  302.     }
  303.     in_end_rule = 0;
  304.     if (close_io() != 0 && exit_val == 0)
  305.         exit_val = 1;
  306.     exit(exit_val);        /* more portable */
  307.     return exit_val;    /* to suppress warnings */
  308. }
  309.  
  310. static void
  311. usage()
  312. {
  313.     char *opt1 = " -f progfile [--]";
  314.     char *opt2 = " [--] 'program'";
  315.     char *regops = " [-F fs] [-v var=val] [-W gawk-opts]";
  316.  
  317.     fprintf(stderr, "usage: %s%s%s file ...\n       %s%s%s file ...\n",
  318.         myname, regops, opt1, myname, regops, opt2);
  319.     exit(11);
  320. }
  321.  
  322. Regexp *
  323. mk_re_parse(s, ignorecase)
  324. char *s;
  325. int ignorecase;
  326. {
  327.     char *src;
  328.     register char *dest;
  329.     register int c;
  330.     int in_brack = 0;
  331.  
  332.     for (dest = src = s; *src != '\0';) {
  333.         if (*src == '\\') {
  334.             c = *++src;
  335.             switch (c) {
  336.             case '/':
  337.             case 'a':
  338.             case 'b':
  339.             case 'f':
  340.             case 'n':
  341.             case 'r':
  342.             case 't':
  343.             case 'v':
  344.             case 'x':
  345.             case '0':
  346.             case '1':
  347.             case '2':
  348.             case '3':
  349.             case '4':
  350.             case '5':
  351.             case '6':
  352.             case '7':
  353.                 c = parse_escape(&src);
  354.                 if (c < 0)
  355.                     cant_happen();
  356.                 *dest++ = (char)c;
  357.                 break;
  358.             default:
  359.                 *dest++ = '\\';
  360.                 *dest++ = (char)c;
  361.                 src++;
  362.                 break;
  363.             }
  364.         } else if (*src == '/' && ! in_brack)
  365.             break;
  366.         else {
  367.             if (*src == '[')
  368.                 in_brack = 1;
  369.             else if (*src == ']')
  370.                 in_brack = 0;
  371.  
  372.             *dest++ = *src++;
  373.         }
  374.     }
  375.     return make_regexp(tmp_string(s, dest-s), ignorecase, 1);
  376. }
  377.  
  378. static void
  379. copyleft ()
  380. {
  381.     static char blurb_part1[] =
  382. "Copyright (C) 1989, 1991, 1992, Free Software Foundation.\n\
  383. \n\
  384. This program is free software; you can redistribute it and/or modify\n\
  385. it under the terms of the GNU General Public License as published by\n\
  386. the Free Software Foundation; either version 2 of the License, or\n\
  387. (at your option) any later version.\n\
  388. \n";
  389.     static char blurb_part2[] =
  390. "This program is distributed in the hope that it will be useful,\n\
  391. but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  392. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  393. GNU General Public License for more details.\n\
  394. \n";
  395.     static char blurb_part3[] =
  396. "You should have received a copy of the GNU General Public License\n\
  397. along with this program; if not, write to the Free Software\n\
  398. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
  399.  
  400.     fprintf(stderr, "%s, patchlevel %d\n", version_string, PATCHLEVEL);
  401.     fputs(blurb_part1, stderr);
  402.     fputs(blurb_part2, stderr);
  403.     fputs(blurb_part3, stderr);
  404.     fflush(stderr);
  405. }
  406.  
  407. static void
  408. cmdline_fs(str)
  409. char *str;
  410. {
  411.     register NODE **tmp;
  412.     int len = strlen(str);
  413.  
  414.     tmp = get_lhs(FS_node, (Func_ptr *) 0);
  415.     unref(*tmp);
  416.     /*
  417.      * Only if in full compatibility mode check for the stupid special
  418.      * case so -F\t works as documented in awk even though the shell
  419.      * hands us -Ft.  Bleah!
  420.      *
  421.      * Thankfully, Posix didn't propogate this "feature".
  422.      */
  423.     if (str[0] == 't' && str[1] == '\0') {
  424.         if (do_lint)
  425.             warning("-Ft does not set FS to tab in POSIX awk");
  426.         if (strict && ! do_posix)
  427.             str[0] = '\t';
  428.     }
  429.     *tmp = make_str_node(str, len, SCAN);    /* do process escapes */
  430.     set_FS();
  431. }
  432.  
  433. static void
  434. init_args(argc0, argc, argv0, argv)
  435. int argc0, argc;
  436. char *argv0;
  437. char **argv;
  438. {
  439.     int i, j;
  440.     NODE **aptr;
  441.  
  442.     ARGV_node = install("ARGV", node(Nnull_string, Node_var, (NODE *)NULL));
  443.     aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
  444.     *aptr = make_string(argv0, strlen(argv0));
  445.     (*aptr)->flags |= MAYBE_NUM;
  446.     for (i = argc0, j = 1; i < argc; i++) {
  447.         aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
  448.         *aptr = make_string(argv[i], strlen(argv[i]));
  449.         (*aptr)->flags |= MAYBE_NUM;
  450.         j++;
  451.     }
  452.     ARGC_node = install("ARGC",
  453.             node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
  454. }
  455.  
  456. /*
  457.  * Set all the special variables to their initial values.
  458.  */
  459. struct varinit {
  460.     NODE **spec;
  461.     char *name;
  462.     NODETYPE type;
  463.     char *strval;
  464.     AWKNUM numval;
  465.     Func_ptr assign;
  466. };
  467. static struct varinit varinit[] = {
  468. {&NF_node,    "NF",        Node_NF,        0,    -1, set_NF },
  469. {&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS,    "",    0,  0 },
  470. {&NR_node,    "NR",        Node_NR,        0,    0,  set_NR },
  471. {&FNR_node,    "FNR",        Node_FNR,        0,    0,  set_FNR },
  472. {&FS_node,    "FS",        Node_FS,        " ",    0,  0 },
  473. {&RS_node,    "RS",        Node_RS,        "\n",    0,  set_RS },
  474. {&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE,    0,    0,  set_IGNORECASE },
  475. {&FILENAME_node, "FILENAME",    Node_var,        "-",    0,  0 },
  476. {&OFS_node,    "OFS",        Node_OFS,        " ",    0,  set_OFS },
  477. {&ORS_node,    "ORS",        Node_ORS,        "\n",    0,  set_ORS },
  478. {&OFMT_node,    "OFMT",        Node_OFMT,        "%.6g",    0,  set_OFMT },
  479. {&CONVFMT_node,    "CONVFMT",    Node_CONVFMT,        "%.6g",    0,  set_CONVFMT },
  480. {&RLENGTH_node, "RLENGTH",    Node_var,        0,    0,  0 },
  481. {&RSTART_node,    "RSTART",    Node_var,        0,    0,  0 },
  482. {&SUBSEP_node,    "SUBSEP",    Node_var,        "\034",    0,  0 },
  483. {0,        0,        Node_illegal,        0,    0,  0 },
  484. };
  485.  
  486. static void
  487. init_vars()
  488. {
  489.     register struct varinit *vp;
  490.  
  491.     for (vp = varinit; vp->name; vp++) {
  492.         *(vp->spec) = install(vp->name,
  493.           node(vp->strval == 0 ? make_number(vp->numval)
  494.                 : make_string(vp->strval, strlen(vp->strval)),
  495.                vp->type, (NODE *) NULL));
  496.         if (vp->assign)
  497.             (*(vp->assign))();
  498.     }
  499. }
  500.  
  501. void
  502. load_environ()
  503. {
  504. #if !defined(MSDOS) && !(defined(VMS) && defined(__DECC))
  505.     extern char **environ;
  506. #endif
  507.     register char *var, *val;
  508.     NODE **aptr;
  509.     register int i;
  510.  
  511.     ENVIRON_node = install("ENVIRON", 
  512.             node(Nnull_string, Node_var, (NODE *) NULL));
  513.     for (i = 0; environ[i]; i++) {
  514.         static char nullstr[] = "";
  515.  
  516.         var = environ[i];
  517.         val = strchr(var, '=');
  518.         if (val)
  519.             *val++ = '\0';
  520.         else
  521.             val = nullstr;
  522.         aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen (var)));
  523.         *aptr = make_string(val, strlen (val));
  524.         (*aptr)->flags |= MAYBE_NUM;
  525.  
  526.         /* restore '=' so that system() gets a valid environment */
  527.         if (val != nullstr)
  528.             *--val = '=';
  529.     }
  530. }
  531.  
  532. /* Process a command-line assignment */
  533. char *
  534. arg_assign(arg)
  535. char *arg;
  536. {
  537.     char *cp;
  538.     Func_ptr after_assign = NULL;
  539.     NODE *var;
  540.     NODE *it;
  541.     NODE **lhs;
  542.  
  543.     cp = strchr(arg, '=');
  544.     if (cp != NULL) {
  545.         *cp++ = '\0';
  546.         /*
  547.          * Recent versions of nawk expand escapes inside assignments.
  548.          * This makes sense, so we do it too.
  549.          */
  550.         it = make_str_node(cp, strlen(cp), SCAN);
  551.         it->flags |= MAYBE_NUM;
  552.         var = variable(arg, 0);
  553.         lhs = get_lhs(var, &after_assign);
  554.         unref(*lhs);
  555.         *lhs = it;
  556.         if (after_assign)
  557.             (*after_assign)();
  558.         *--cp = '=';    /* restore original text of ARGV */
  559.     }
  560.     return cp;
  561. }
  562.  
  563. static void
  564. pre_assign(v)
  565. char *v;
  566. {
  567.     if (!arg_assign(v)) {
  568.         fprintf (stderr,
  569.             "%s: '%s' argument to -v not in 'var=value' form\n",
  570.                 myname, v);
  571.         usage();
  572.     }
  573. }
  574.  
  575. SIGTYPE
  576. catchsig(sig, code)
  577. int sig, code;
  578. {
  579. #ifdef lint
  580.     code = 0; sig = code; code = sig;
  581. #endif
  582.     if (sig == SIGFPE) {
  583.         fatal("floating point exception");
  584.     } else if (sig == SIGSEGV
  585. #ifndef WIN32
  586. #ifndef MSDOS
  587.             || sig == SIGBUS
  588. #endif
  589. #endif
  590.     ) {
  591.     msg("fatal error: internal error");
  592.         /* fatal won't abort() if not compiled for debugging */
  593.     abort();
  594.     } else
  595.         cant_happen();
  596.     /* NOTREACHED */
  597. }
  598.  
  599. /* gawk_option --- do gawk specific things */
  600.  
  601. static void
  602. gawk_option(optstr)
  603. char *optstr;
  604. {
  605.     char *cp;
  606.  
  607.     for (cp = optstr; *cp; cp++) {
  608.         switch (*cp) {
  609.         case ' ':
  610.         case '\t':
  611.         case ',':
  612.             break;
  613.         case 'v':
  614.         case 'V':
  615.             /* print version */
  616.             if (strncasecmp(cp, "version", 7) != 0)
  617.                 goto unknown;
  618.             else
  619.                 cp += 6;
  620.             fprintf(stderr, "%s, patchlevel %d\n",
  621.                     version_string, PATCHLEVEL);
  622.             break;
  623.         case 'c':
  624.         case 'C':
  625.             if (strncasecmp(cp, "copyright", 9) == 0) {
  626.                 cp += 8;
  627.                 copyleft();
  628.             } else if (strncasecmp(cp, "copyleft", 8) == 0) {
  629.                 cp += 7;
  630.                 copyleft();
  631.             } else if (strncasecmp(cp, "compat", 6) == 0) {
  632.                 cp += 5;
  633.                 strict = 1;
  634.             } else
  635.                 goto unknown;
  636.             break;
  637.         case 'n':
  638.         case 'N':
  639.             if (strncasecmp(cp, "nostalgia", 9) != 0)
  640.                 goto unknown;
  641.             nostalgia();
  642.             break;
  643.         case 'p':
  644.         case 'P':
  645. #ifdef DEBUG
  646.             if (strncasecmp(cp, "parsedebug", 10) == 0) {
  647.                 cp += 10;
  648.                 yydebug = 2;
  649.                 break;
  650.             }
  651. #endif
  652.             if (strncasecmp(cp, "posix", 5) != 0)
  653.                 goto unknown;
  654.             cp += 4;
  655.             do_posix = 1;
  656.             strict = 1;
  657.             break;
  658.         case 'l':
  659.         case 'L':
  660.             if (strncasecmp(cp, "lint", 4) != 0)
  661.                 goto unknown;
  662.             cp += 3;
  663.             do_lint = 1;
  664.             break;
  665.         default:
  666.         unknown:
  667.             fprintf(stderr, "'%c' -- unknown option, ignored\n",
  668.                 *cp);
  669.             break;
  670.         }
  671.     }
  672. }
  673.  
  674. /* nostalgia --- print the famous error message and die */
  675.  
  676. static void
  677. nostalgia()
  678. {
  679.     fprintf(stderr, "awk: bailing out near line 1\n");
  680.     abort();
  681. }
  682.  
  683. static char *
  684. gawk_name(filespec)
  685. char *filespec;
  686. {
  687.         char *p;
  688.     
  689. #ifdef VMS    /* "device:[root.][directory.subdir]GAWK.EXE;n" -> "GAWK" */
  690.         char *q;
  691.  
  692.         p = strrchr(filespec, ']');  /* directory punctuation */
  693.         q = strrchr(filespec, '>');  /* alternate <international> punct */
  694.  
  695.         if (p == NULL || q > p) p = q;
  696.     p = strdup(p == NULL ? filespec : (p + 1));
  697.     if ((q = strrchr(p, '.')) != NULL)  *q = '\0';  /* strip .typ;vers */
  698.  
  699.     return p;
  700. #endif /*VMS*/
  701.  
  702. #if defined(MSDOS) || defined(atarist)
  703.         char *q;
  704.  
  705.         p = filespec;
  706.     
  707.         if (q = strrchr(p, '\\'))
  708.             p = q + 1;
  709.         if (q = strchr(p, '.'))
  710.             *q = '\0';
  711.         strlwr(p);
  712.  
  713.         return (p == NULL ? filespec : p);
  714. #endif /* MSDOS || atarist */
  715.  
  716.     /* "path/name" -> "name" */
  717.     p = strrchr(filespec, '/');
  718.     return (p == NULL ? filespec : p + 1);
  719. }
  720.